package com.ruoyi.web.controller.xxc;



import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.news.domain.XcxWalletInfo;
import com.ruoyi.news.domain.XxcWallet;
import com.ruoyi.news.service.IXcxWalletInfoService;
import com.ruoyi.news.service.IXxcWalletService;
import com.ruoyi.news.utils.WechatPayConfig;
import com.ruoyi.news.utils.WechatPayValidator;
import com.ruoyi.news.utils.WxHttpUtils;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;


/**
 * @Author: Zzz
 * @Description:
 **/
@Api(tags = "回调接口(API3)")
@RestController
public class NotifyController {

    @Resource
    private WechatPayConfig wechatPayConfig;

    @Resource
    private Verifier verifier;


    @Autowired
    private IXcxWalletInfoService xcxWalletInfoService;

    @Autowired
    private IXxcWalletService xxcWalletService;

    @Autowired
    private RedisCache redisCache;



    private final ReentrantLock lock = new ReentrantLock();

    @ApiOperation(value = "支付回调", notes = "支付回调")
    @ApiOperationSupport(order = 5)
    @PostMapping("/payNotify")
    public Map<String, String> payNotify(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("支付回调");

        // 处理通知参数
          Map<String,Object> bodyMap = getNotifyBody(request);
        if(bodyMap==null){
            return falseMsg(response);
        }

        System.out.println("=========== 在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱 ===========");
        if(lock.tryLock()) {
            try {
                // 解密resource中的通知数据
                String resource = bodyMap.get("resource").toString();
                Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(),1);
                String orderNo = resourceMap.get("out_trade_no").toString();
                String transactionId = resourceMap.get("transaction_id").toString();

                // TODO 根据订单号，做幂等处理，并且在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱
                System.out.println("=========== 根据订单号，做幂等处理 ===========");
                redisCache.setCacheObject(orderNo, orderNo);

                XcxWalletInfo xcxWalletInfo = xcxWalletInfoService.selectXcxWalletInfoById(orderNo);
                if(xcxWalletInfo!=null) {
                    if ("1".equals(xcxWalletInfo.getState())) {
                        //修改订单回调状态
                        XcxWalletInfo walletInfo = xcxWalletInfoService.selectXcxWalletInfoById(orderNo);
                        walletInfo.setState("2");
                        xcxWalletInfoService.updateXcxWalletInfo(walletInfo);
                    }
                }else {
                    Object res = redisCache.getCacheObject(orderNo);
                    if(res == null) {
                        redisCache.setCacheObject(orderNo, orderNo);
                    }
                }


            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }

        //成功应答
        return trueMsg(response);
    }


    @ApiOperation(value = "退款回调", notes = "退款回调")
    @ApiOperationSupport(order = 5)
    @PostMapping("/refundNotify")
    public Map<String, String> refundNotify(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("退款回调");

        // 处理通知参数
        Map<String,Object> bodyMap = getNotifyBody(request);
        if(bodyMap==null){
            return falseMsg(response);
        }

        System.out.println("=========== 在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱 ===========");
        if(lock.tryLock()) {
            try {
                // 解密resource中的通知数据
                String resource = bodyMap.get("resource").toString();
                Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(),2);
                String orderNo = resourceMap.get("out_trade_no").toString();
                String transactionId = resourceMap.get("transaction_id").toString();

                // TODO 根据订单号，做幂等处理，并且在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱

                System.out.println("=========== 根据订单号，做幂等处理 ===========");
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }

        //成功应答
        return trueMsg(response);
    }

    private Map<String,Object> getNotifyBody(HttpServletRequest request){
        //处理通知参数
        String body = WxHttpUtils.readData(request);
        System.out.println("退款回调参数：{}"+body);

        // 转换为Map
        Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>(){});
        // 微信的通知ID（通知的唯一ID）
        String notifyId = bodyMap.get("id").toString();

        // 验证签名信息
        WechatPayValidator wechatPayValidator
                = new WechatPayValidator(verifier, notifyId, body);
        if(!wechatPayValidator.validate(request)){

            System.out.println("通知验签失败");
            return null;
        }
        System.out.println("通知验签成功");
        return bodyMap;
    }

    private Map<String, String> falseMsg(HttpServletResponse response){
        Map<String, String> resMap = new HashMap<>(8);
        //失败应答
        response.setStatus(500);
        resMap.put("code", "ERROR");
        resMap.put("message", "通知验签失败");
        return resMap;
    }

    private Map<String, String> trueMsg(HttpServletResponse response){
        Map<String, String> resMap = new HashMap<>(8);
        //成功应答
        response.setStatus(200);
        resMap.put("code", "SUCCESS");
        resMap.put("message", "成功");
        return resMap;
    }
}
